package com.googlecode.jfxgap;

import java.util.TimerTask;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

import org.apache.cordova.LocalStorage;
import org.apache.cordova.api.CordovaWebView;

import com.sun.webpane.webkit.JSObject;


/**
 * JavaFx Cordova App abstract.
 * @author Cyril Joui
 */
abstract public class JavaFxCordovaApp extends Application {

    /**
     * Global cordova web view.
     */
	private CordovaWebView cordovaWebView;

	/**
	 * JavaFX stage reference.
	 */
	private Stage initStage;

	/**
	 * Application splash.
	 */
	private DesktopSplash splash;

	/**
	 * Web view reference.
	 */
	private WebView webView;

	/**
	 * Main stage.
	 */
	private Stage mainStage;

	/**
	 * {@inheritDoc}
	 * @see javafx.application.Application#init()
	 */
	@Override
	public void init() throws Exception {
		super.init();
		splash = new DesktopSplash();
	}

    /**
     * {@inheritDoc}
     * @see javafx.application.Application#start(javafx.stage.Stage)
     */
    @Override
    public void start(final Stage initStage) {
    	this.initStage = initStage;

    	showSplash(initStage);
    	loadMainStage();
    }

    /**
     * {@inheritDoc}
     * @see javafx.application.Application#stop()
     */
    @Override
    public void stop() throws Exception {
    	super.stop();
    	LocalStorage.releaseInstance();
    }

	/**
	 * @return the cordovaWebView
	 */
	protected CordovaWebView getCordovaWebView() {
		return cordovaWebView;
	}

    /**
	 * @return the stage
	 */
	protected Stage getStage() {
		return mainStage;
	}

	/**
     * Load the url into the webview.
     * @param url
     */
    public void loadUrl(String url) {
    	if (cordovaWebView == null) {
    		System.err.println("Can't load Url: " + url + ". Cordova web view not initialized!");
    		return;
    	}

        // Set backgroundColor
    	/*
        this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK);
        this.root.setBackgroundColor(this.backgroundColor);
        */

        // If keepRunning
    	/*
        this.keepRunning = this.getBooleanProperty("keepRunning", true);
        */

    	// Load given url with engine
        cordovaWebView.getView().getEngine().load(url);

        splash.getLoadProgress().progressProperty().bind(webView.getEngine().getLoadWorker().workDoneProperty().divide(100));

        // FIXME CJO - delay to close splashscreen should be customizable
        long destroySplashAutoTime = 8000;
		new java.util.Timer().schedule(new TimerTask() {
			
			@Override
			public void run() {
				System.out.println("RUNNING close splash ...");
				Platform.runLater(new Runnable() {
			        @Override
			        public void run() {
			        	if (initStage.isShowing()) {
			        		splash.destroy(new EventHandler<ActionEvent>() {
			        			@Override public void handle(ActionEvent actionEvent) {
			        				mainStage.show();
			        			}
			        		});
			        	}
			        }
			   });
			}
		}, destroySplashAutoTime);
    }

    private void loadMainStage() {
    	mainStage = new Stage(StageStyle.DECORATED);

        webView = new WebView();
        final WebEngine webEngine = webView.getEngine();
        webEngine.setOnAlert(new EventHandler<WebEvent<String>>() {
            public void handle(WebEvent<String> stringWebEvent) {
                System.out.println(">> ALERT: " + stringWebEvent.getData());
            }
        });
        // handle events
        webEngine.getLoadWorker().stateProperty().addListener(
                new ChangeListener<Worker.State>() {

					public void changed(ObservableValue<? extends Worker.State> ov, Worker.State oldState, Worker.State newState) {

                        if (newState == Worker.State.SUCCEEDED) {
                        	webEngine.executeScript("if (!document.getElementById('FirebugLite')){E = document['createElement' + 'NS'] && document.documentElement.namespaceURI;E = E ? document['createElement' + 'NS'](E, 'script') : document['createElement']('script');E['setAttribute']('id', 'FirebugLite');E['setAttribute']('src', 'https://getfirebug.com/' + 'firebug-lite.js' + '#startOpened');E['setAttribute']('FirebugLite', '4');(document['getElementsByTagName']('head')[0] || document['getElementsByTagName']('body')[0]).appendChild(E);E = new Image;E['setAttribute']('src', 'https://getfirebug.com/' + '#startOpened');}"); 

                        	System.out.println(">> Executing device ready event ...");
                        	JSObject win = (JSObject) webEngine.executeScript("window");
                        	win.setMember("lStorage", LocalStorage.getInstance());

                        	webEngine.executeScript("window._nativeReady = true;");
                        	webEngine.executeScript("startDesktop()");

                        	System.out.println("<< Executing device ready event ...");
                        }

                    }
                });

        webEngine.getLoadWorker().stateProperty().addListener(new InvalidationListener() {
            public void invalidated(Observable observable) {
                System.out.println(observable);
            }
        });

        // Start cordova here
        cordovaWebView = new CordovaWebView(this);

        mainStage.setScene(new Scene(webView));
      }

      private void showSplash(Stage initStage) {
        splash.show(initStage);
      }

	/**
	 * @return the splash
	 */
	protected DesktopSplash getSplash() {
		return splash;
	}

	/**
	 * @return the webView
	 */
	public WebView getWebView() {
		return webView;
	}

	/**
	 * @return the mainStage
	 */
	public Stage getMainStage() {
		return mainStage;
	}
}